﻿using mozhi.smarterp.Finance.Accounts;
using mozhi.smarterp.Finance.Accounts.Repository;
using mozhi.smarterp.Finance.DataDicts;
using mozhi.smarterp.Finance.DataDicts.Repository;
using mozhi.smarterp.Finance.Enums;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;

namespace mozhi.smarterp.Finance.Ieps.Import
{
    public class AccountImportHandler : ImportHandlerBase<AccountDto>
    {
        private IAccountAppService _accountAppService;
        private List<DataDictionary> _accountTypeDictionaries;
        private IDataDictionaryRepository _dataDictionaryRepository;
        private IAccountDimensionRepository _dimensionRepository;
        private List<AccountDimension> _dimensions;
        private List<AccountDto> _oldAccounts;

        public AccountImportHandler(IServiceProvider serviceProvider) : base(serviceProvider)
        {
            _accountAppService = serviceProvider.GetService(typeof(IAccountAppService)) as IAccountAppService;
            _dataDictionaryRepository = serviceProvider.GetService(typeof(IDataDictionaryRepository)) as IDataDictionaryRepository;
            _dimensionRepository = serviceProvider.GetService(typeof(IAccountDimensionRepository)) as IAccountDimensionRepository;
        }

        public override async Task<ImportResultDto> HandleImportData(Guid fileId)
        {
            await LoadDataDictionaryAsync();
            await LoadDimensionAsync();
            await LoadOldAccountAsync();

            var accounts = (await GetImportData(ModuleCodeType.Account, fileId)) as List<AccountDto>;

            var result = new ImportResultDto();

            result.Total = accounts.Count;

            result.Errors = new List<string>();

            for (int i = 0; i < accounts.Count; i++)
            {
                var account = accounts[i];
                try
                {
                    if (_oldAccounts.Exists(x => x.Code == account.Code))
                        await UpdateAccount(account);
                    else
                        await AddAccount(account);

                    result.SuccessfulCount += 1;
                }
                catch (UserFriendlyException ex)
                {
                    result.FailureCount += 1;
                    result.Errors.Add($"第{i + 1}行有错误：{ex.Message}");
                }
            }

            return result;
        }


        protected override List<AccountDto> ConvertToData(DataSet ds)
        {
            var result = new List<AccountDto>();

            var dt = ds.Tables[0];

            if (dt.Rows.Count == 0)
                return result;

            foreach (DataRow row in dt.Rows)
            {
                var account = new AccountDto();
                account.Code = row["Code"].ToString();
                account.Name = row["Name"].ToString();
                account.AccountType = GetAccountType(row["AccountType"].ToString());
                account.Direction = GetAccountDirectionType(row["Direction"].ToString());
                account.IsForeignCurrency = GetBooleanValue(row["IsForeignCurrency"].ToString());
                account.IsFundAccount = GetBooleanValue(row["IsFundAccount"].ToString());

                var requiredDimensionItems = GetAccountDimensionItems(row["RequiredDimensionNames"].ToString());

                requiredDimensionItems.ForEach(item => { item.IsRequire = true; });

                var optionDimensionItems = GetAccountDimensionItems(row["OptionDimensionNames"].ToString());

                account.DimensionItems = new List<AccountDimensionItemDto>();

                account.DimensionItems.AddRange(requiredDimensionItems);
                account.DimensionItems.AddRange(optionDimensionItems);

                account.ParentId = GetAccountParentId(account.Code);
                account.Id = GetAccountId(account.Code);

                result.Add(account);
            }

            return result;
        }

        #region

        private async Task LoadDataDictionaryAsync()
        {
            var query = new Querys.DataDictPageQuery() { PageIndex = 1, PageSize = int.MaxValue, ModuleCode = ModuleCodeType.AccountType };

            _accountTypeDictionaries = await _dataDictionaryRepository.FindAsync(query);
        }

        private async Task LoadDimensionAsync()
        {
            _dimensions = await _dimensionRepository.FindAsync(new Querys.PageQuery() { PageIndex = 1, PageSize = int.MaxValue });
        }

        private async Task LoadOldAccountAsync()
        {
            _oldAccounts = await _accountAppService.GetListAsync(new Querys.AccountQuery() { IncludeDetail = true });
        }

        private AccountType GetAccountType(string name)
        {
            var match = _accountTypeDictionaries.FirstOrDefault(x => x.Value == name);

            if (match == null)
                return default;

            var accountType = (AccountType)int.Parse(match.Key);

            return accountType;
        }

        private AccountDirectionType GetAccountDirectionType(string name)
        {
            if (name == "借")
                return AccountDirectionType.Debit;
            if(name == "贷")
                return AccountDirectionType.Credit;

            return default;
        }

        private List<AccountDimensionItemDto> GetAccountDimensionItems(string name)
        {
            var result = new List<AccountDimensionItemDto>();

            if (name.IsNullOrEmpty())
                return result;

            var dimensionNames = name.Split(' ');

            if(_dimensions.Count==0)
                return result;

            foreach(var dimensionName in dimensionNames)
            {
                var matchDimensionItem = _dimensions.FirstOrDefault(x=>x.Name == dimensionName);

                if (matchDimensionItem == null)
                    result.Add(new AccountDimensionItemDto());
                else
                {
                    result.Add(new AccountDimensionItemDto()
                    {
                        DimensionId = matchDimensionItem.Id,
                        DimensionName = matchDimensionItem.Name,
                        DataSourceType = matchDimensionItem.DataSourceType,
                        TagId = matchDimensionItem.TagId,
                    }); 
                }

            }

            return result;
        }

        private Guid? GetAccountParentId(string code)
        {
            if(code.IsNullOrEmpty())
                return null;

            var splits = code.Split('.').ToList();

            var count = splits.Count;

            if (count == 1)
                return null;

            var parentCode = code.Replace("." + splits[count - 1], "");

            var matchParentAccount = _oldAccounts.FirstOrDefault(x => x.Code == parentCode);

            return matchParentAccount?.Id;
        }

        private Guid GetAccountId(string code)
        {
            if (code.IsNullOrEmpty())
                return default;

            var matchAccount = _oldAccounts.FirstOrDefault(x => x.Code == code);

            return matchAccount == null ? default : matchAccount.Id;
        }

        private async Task AddAccount(AccountDto account)
        {
            var createDto = new AccountCreateDto();
            createDto.ParentId = account.ParentId;
            createDto.Name = account.Name;
            createDto.Code = account.Code;
            createDto.AccountType = account.AccountType;
            createDto.Direction = account.Direction;
            createDto.IsFundAccount = account.IsFundAccount;
            createDto.IsForeignCurrency = account.IsForeignCurrency;
            createDto.Activated = true;

            createDto.DimensionItems = new List<AccountDimensionItemCreateDto>();

            foreach (var item in account.DimensionItems)
            {
                createDto.DimensionItems.Add(new AccountDimensionItemCreateDto()
                {
                    IsRequire = item.IsRequire,
                    DimensionId = item.DimensionId,
                    DimensionName = item.DimensionName,
                    Activated = true,
                });
            }

            await _accountAppService.CreateAsync(createDto);
        }

        private async Task UpdateAccount(AccountDto account)
        {
            var matchAccount = _oldAccounts.FirstOrDefault(x => x.Code == account.Code);

            if (matchAccount == null)
                return;

            //没有做任何更改
            if (AccountEqual(matchAccount, account))
                return;

            var dto = new AccountUpdateDto();
            dto.Name = account.Name;
            dto.Code = account.Code;
            dto.AccountType = account.AccountType;
            dto.Direction = account.Direction;
            dto.IsFundAccount = account.IsFundAccount;
            dto.IsForeignCurrency = account.IsForeignCurrency;
            dto.Activated = true;

            dto.DimensionItems = new List<AccountDimensionItemCreateDto>();

            foreach (var item in account.DimensionItems)
            {
                dto.DimensionItems.Add(new AccountDimensionItemCreateDto()
                {
                    IsRequire = item.IsRequire,
                    DimensionId = item.DimensionId,
                    DimensionName = item.DimensionName,
                    Activated = true,
                });
            }

            await _accountAppService.UpdateAsync(dto);
        }

        private bool AccountEqual(AccountDto oldAccount , AccountDto newAccount)
        {
            //先比较基础信息
            var result = oldAccount.Name == newAccount.Name && oldAccount.Direction == newAccount.Direction && oldAccount.AccountType == newAccount.AccountType
                && oldAccount.IsFundAccount == newAccount.IsFundAccount && oldAccount.IsForeignCurrency == newAccount.IsForeignCurrency;

            if (!result)
                return false;

            //比较核算维度
            var dimensionEqual = oldAccount.DimensionItems.Count == newAccount.DimensionItems.Count;

            if (!dimensionEqual)
                return false;

            dimensionEqual = true;

            foreach(var item in oldAccount.DimensionItems)
            {
                var matchItem = newAccount.DimensionItems.FirstOrDefault(x => x.DimensionId == item.DimensionId);

                if(matchItem==null)
                {
                    dimensionEqual = false;
                    break;
                }

                dimensionEqual = matchItem.IsRequire == item.IsRequire;
            }

            if (!dimensionEqual)
                return false;

            foreach (var item in newAccount.DimensionItems)
            {
                var matchItem = oldAccount.DimensionItems.FirstOrDefault(x => x.DimensionId == item.DimensionId);

                if (matchItem == null)
                {
                    dimensionEqual = false;
                    break;
                }

                dimensionEqual = matchItem.IsRequire == item.IsRequire;
            }

            return result && dimensionEqual;
        }
        #endregion
    }
}
